using System;
using System.Collections.Generic;
using System.Text;
using NBusiness.Query;

namespace NBusiness.Data.Providers.SqlServer.Query
{
    public enum SqlBooleanOperator
    {
        Equal,
        NotEqual,
        LessThan,
        LessThanOrEqual,
        GreaterThan,
        GreaterThanOrEqual,
        Like,
        NotLike,
        And,
        Or,
        In,
        NotIn,
        Is,
        IsNot
    }
        
    public abstract class SqlBoolean : SqlEvaluatable, IExpression
    {
        #region Member fields and properties
        private IEvaluatable _left;

        private IEvaluatable _right;

        private SqlBooleanOperator _operator;
        protected SqlBooleanOperator Operator
        {
            get { return _operator; }
            set { _operator = value; }
        }
        #endregion

        #region Constructors
        public SqlBoolean(SqlBooleanOperator @operator)
            : this(null, @operator, null)
        { }
        public SqlBoolean(IEvaluatable left, SqlBooleanOperator @operator, IEvaluatable right)
        {
            Left = left;
            Right = right;
            _operator = @operator;
        }
        #endregion

        #region IExpression Members

        public IEvaluatable Left
        {
            get { return _left; }
            set 
            { 
                _left = value;
            }
        }

        public IEvaluatable Right
        {
            get { return _right; }
            set 
            { 
                _right = value;
            }
        }

        #endregion

        #region IRelationalElement Members

        public override string ToSql()
        {
            return ToSql(new SqlQueryContext());
        }
        public override string ToSql(IQueryContext context)
        {
            StringBuilder builder = new StringBuilder("(");
            BuildEvaluatable(builder, _left, context);

            switch (_operator)
            {
                case SqlBooleanOperator.Equal:
                    builder.Append("=");
                    break;
                case SqlBooleanOperator.NotEqual:
                    builder.Append("<>");
                    break;
                case SqlBooleanOperator.GreaterThan:
                    builder.Append(">");
                    break;
                case SqlBooleanOperator.GreaterThanOrEqual:
                    builder.Append(">=");
                    break;
                case SqlBooleanOperator.LessThan:
                    builder.Append("<");
                    break;
                case SqlBooleanOperator.LessThanOrEqual:
                    builder.Append("<=");
                    break;
                case SqlBooleanOperator.Like:
                    builder.Append(" LIKE ");
                    break;
                case SqlBooleanOperator.NotLike:
                    builder.Append(" NOT LIKE ");
                    break;
                case SqlBooleanOperator.And:
                    builder.Append(" AND ");
                    break;
                case SqlBooleanOperator.Or:
                    builder.Append(" OR ");
                    break;
                case SqlBooleanOperator.In:
                    builder.Append(" IN ");
                    break;
                case SqlBooleanOperator.NotIn:
                    builder.Append(" NOT IN ");
                    break;
                case SqlBooleanOperator.Is:
                    builder.Append(" IS ");
                    break;
                case SqlBooleanOperator.IsNot:
                    builder.Append(" IS NOT ");
                    break;
            }
            BuildEvaluatable(builder, _right, context);
            builder.Append(")");

            return builder.ToString();
        }

        private void BuildEvaluatable(StringBuilder builder, IEvaluatable evaluatable, IQueryContext context)
        {
            if (evaluatable is IColumn)
            {
                if ((evaluatable as IColumn).Parent != null)
                {
                    (context as SqlQueryContext).EnsureAlias((evaluatable as IColumn).Parent);

                    if (!string.IsNullOrEmpty((evaluatable as IColumn).Parent.Alias))
                    {
                        builder.Append("[");
                        builder.Append((evaluatable as IColumn).Parent.Alias);
                        builder.Append("].");
                    }
                }
                builder.Append("[");
                builder.Append((evaluatable as IColumn).Name);
                builder.Append("]");
            }
            else if (evaluatable is IStatement)
            {
                (context as SqlQueryContext).EnsureAlias(evaluatable as ISelect);
                builder.Append(evaluatable.ToSql(context));
            }
            else builder.Append(evaluatable.ToSql(context));
        }

        #endregion

        #region Operator overloads
        public static SqlBoolean operator &(SqlBoolean left, IExpression right)
        {
            return left.And(right) as SqlBoolean;
        }
        public static SqlBoolean operator |(SqlBoolean left, IExpression right)
        {
            return left.Or(right) as SqlBoolean;
        } 
        #endregion

        #region IExpression Members


        public IExpression And(IExpression evaluatable)
        {
            return new SqlAnd(this, evaluatable);
        }

        public IExpression Or(IExpression evaluatable)
        {
            return new SqlOr(this, evaluatable);
        }

        #endregion
    }
}
